package main;

import kit.Observable;
import kit.Observer;

public class Main {
    private static int basicRecord = 1;
    private static String exceptionRecord = new String();
    private static int[] concurrencyRecord = new int[10];

    private static boolean basicTest() {
        Observable<Integer> observable = new Observable<Integer>(1);
        observable.addObserver(new Observer<Integer>(){
            @Override
            public void onChanged(Integer t) {
                basicRecord = t;
            }
        });
        observable.setValue(0);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return (basicRecord == 0) && (observable.getValue() == 0);
    }

    private static boolean exceptionTest() {
        Observable<String> obs = new Observable<String>("");
        obs.addObserver(new Observer<String>(){

            @Override
            public void onChanged(String t) {
                throw new RuntimeException("I threw a RuntimeException!");
            }
            
        });

        obs.addObserver(new Observer<String>(){

            @Override
            public void onChanged(String t) {
                throw new RuntimeException("I threw a RuntimeException, once again.");
            }
            
        });

        obs.addObserver(new Observer<String>(){

            @Override
            public void onChanged(String t) {
                exceptionRecord = t;
            }
            
        });

        final String newValue = "new_value";
        obs.setValue(newValue);
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        return exceptionRecord == newValue;
    }

    private static Observer<Integer> oGenerator(int num, boolean sleep) {
        return new Observer<Integer>(){

            @Override
            public void onChanged(Integer t) {
                if (sleep) {
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                } else {
                    concurrencyRecord[num] = t;
                }
            }
            
        };
    }

    private static boolean concurrencyTest() {
        Observable<Integer> observable = new Observable<Integer>(1);
        for (int i = 0; i < 10; i++) {
            observable.addObserver(oGenerator(i, i < 2));
        }
        final int newValue = 233;
        
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                observable.setValue(newValue);
            }

        });

        thread.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        thread.interrupt();
        
        for (int i = 2; i < 10; i++) {
            if (concurrencyRecord[i] != newValue) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        boolean basicResult = basicTest();
        if (!basicResult) {
            System.out.println("Basic test FAILED");
            System.out.println("quitting");
            return;
        } else {
            System.out.println("Basic test PASSED");
        }
        
        boolean concurrencyResult = concurrencyTest();
        if (concurrencyResult) {
            System.out.println("Concurrency test PASSED");
        } else {
            System.out.println("Concurrency test FAILED");
        }
        
        System.out.println("In the next step, it's NORMAL to see some exception tracebacks.");
        System.out.println("Success info shall be printed if this test is passed.\n\n");

        boolean exceptionResult = exceptionTest();
        if (!exceptionResult) {
            System.out.println("\nException test FAILED");
        } else {
            System.out.println("\nException test PASSED");
        }

        if (basicResult && concurrencyResult && exceptionResult) {
            System.out.println("\n***ALL TESTS PASSED***");
        }
    }
}
